package sp.whereru;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.*;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import routeFunctions.Route;

import XMLread.*;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.Toast;

import com.google.android.maps.*;


public class MapViewActivity extends MapActivity{
	private MapView mMap;
	private ArrayList<GeoPoint> route = new ArrayList<GeoPoint>();
	private ArrayList<GeoPoint> outdoorRoute = null;
	private MapDbAdapter mapDbAdapter;

	//no room tag
	public static final String NO_ROOMS = "N/A";

	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map_view);
		mMap = (MapView) findViewById(R.id.mapView);
		mMap.getOverlays().clear();
		mMap.displayZoomControls(true);
		mMap.setBuiltInZoomControls(true);
		mMap.setSatellite(true);
	}

	@Override
	protected void onStart() {
		super.onStart();

		mapDbAdapter = new MapDbAdapter(this);
		mapDbAdapter.open();

		Intent intent = getIntent();
		Bundle extras = intent.getExtras();

		String sEndBuilding = extras.getString(DirectionsActivity.EXTRA_END_BUILDING);
		String sEndRoom = extras.getString(DirectionsActivity.EXTRA_END_ROOM);
		String sStartBuilding = null;
		String sStartRoom = null;
		if(extras.containsKey(DirectionsActivity.EXTRA_START_BUILDING)) {
			sStartBuilding = extras.getString(DirectionsActivity.EXTRA_START_BUILDING);
			sStartRoom = extras.getString(DirectionsActivity.EXTRA_START_ROOM);

		}


		Loc[] userLoc = null;

		AssetManager assets = getResources().getAssets();
		Drawable torch = this.getResources().getDrawable(R.drawable.ic_torch);
		ParseXML parser = new ParseXML();


		BuildingCSV destBuilding = mapDbAdapter.getBuilding(sEndBuilding);
		BuildingCSV startBuilding = null;
		if(sStartBuilding != null){
			startBuilding = mapDbAdapter.getBuilding(sStartBuilding);
		}
		ArrayList<Space> destBuildingSpaces 	= null;
		ArrayList<Space> startBuildingSpaces 	= null;
		Route routeOutOfStart = 	new Route();
		Route routeOutOfEnd = 		new Route();
		Route routeWithinBuilding = new Route();
		Space endSpace 		= null;
		Space startSpace	= null;
		Building startBuild = null;
		Building endBuild 	= null;

		Loc start 	= null;
		Loc end 	= null;

		Map<String, List<GeoPoint>> areaOverlays = new HashMap<String, List<GeoPoint>>();
		Map<String, List<GeoPoint>> startFloorOverlays = new HashMap<String, List<GeoPoint>>();
		Map<String, List<GeoPoint>> endFloorOverlays = new HashMap<String, List<GeoPoint>>();

		//Overlay our end building shape
		ArrayList<GeoPoint> geoPnts = new ArrayList<GeoPoint>();
		Loc [] shape = destBuilding.getShape();
		for (Loc l : shape) {
			geoPnts.add(l.toGeoPoint());
		}
		areaOverlays.put(sEndBuilding, geoPnts);

		//end room is specified try to get the map of the building
		//if not possible set the end Location to average of the building shape
		if(!sEndRoom.equals(NO_ROOMS)){
			destBuildingSpaces = mapDbAdapter.getBuildingSpaces(destBuilding.getBuildingid());
			String buildingMapFile = destBuilding.getBuildingid() + ".xml";
			for(int i=0; i<=destBuildingSpaces.size(); i++){
				if(destBuildingSpaces.get(i).getRoom().equals(sEndRoom)){
					endSpace = destBuildingSpaces.get(i);
					break;
				}
			}
			
			//overlay floor spaces with end different color 
			int floor = endSpace.getFloor();
			for(int i=0; i<destBuildingSpaces.size(); i++){
				if(destBuildingSpaces.get(i).getFloor( )== floor)
					endFloorOverlays.put(destBuildingSpaces.get(i).getRoom(), destBuildingSpaces.get(i).getArrayList());
			}
			mMap.getOverlays().add(new SpaceAreasOverlay(endFloorOverlays, endSpace));
			
			
			try{
				endBuild = parser.parse((InputStream) assets.open(buildingMapFile), destBuildingSpaces);
				routeOutOfEnd = routeOutOfEnd.getRoute(endSpace, endBuild, new Route());
				//end building different from start so add path out of end building to route
				if(sStartBuilding != null && !sStartBuilding.equals(sEndBuilding)){
					addPathToRoute(routeOutOfEnd);
				}

				//set end location for outdoor route
				ArrayList<Object> directions = routeOutOfEnd.getDirections();
				Exit exitFromEndBuild = (Exit) directions.get(directions.size()-1);
				end = new Loc(exitFromEndBuild.getLat(), exitFromEndBuild.getLong());

			}catch (IOException ex) {
				//Map for building is not in our assets directory
				Toast toast = new Toast(getApplicationContext());
				toast.setDuration(6);
				toast =	Toast.makeText(getApplicationContext(), 
						"The building: " + sEndBuilding + " does not have a map specified. " +
								"Directions will only be to the building area.", toast.getDuration());
				toast.show();

				//TODO try to get alert dialog instead of toast
				//				AlertDialog.Builder dialog = new AlertDialog.Builder(getApplicationContext());
				//		        dialog.setTitle("Sorry!");
				//		        dialog.setMessage("The building: " + sEndBuilding + " does not have a map specified. " +
				//						"Directions will only be to the building area.");
				//		        dialog.show();

				end = averagePoints(destBuilding.getShape());
				endBuild = null;
			} catch (ParserConfigurationException e) {
				e.printStackTrace();
			} catch (SAXException e) {
				e.printStackTrace();
			}
			//our end building has no rooms to get to set end location to average points of this building
		}else{
			end = averagePoints(destBuilding.getShape());
		}



		//start building was specified and it is the same as the end building
		//we want a route inside a single building
		if(sStartBuilding != null && !sStartRoom.equals(NO_ROOMS) && sStartBuilding.equals(sEndBuilding)){
			//set the start space for our route
			for(int i=0; i<=destBuildingSpaces.size(); i++){
				if(destBuildingSpaces.get(i).getRoom().equals(sStartRoom)){
					startSpace = destBuildingSpaces.get(i);
					break;
				}
			}
			//we have a map of the building so get the route within
			if(endBuild != null){
				routeWithinBuilding = routeWithinBuilding.getRouteWithinBuilding(startSpace, endSpace, endBuild, new Route());
				addPathToRoute(routeWithinBuilding);	
			}

		}


		// the user entered the start building that is different than the end building
		else if(sStartBuilding != null && !sStartBuilding.equals(sEndBuilding)){
			//Overlay the start building shape
			geoPnts = new ArrayList<GeoPoint>();
			Loc [] startShape = startBuilding.getShape();
			for (Loc l : startShape) {
				geoPnts.add(l.toGeoPoint());
			}
			areaOverlays.put(sStartBuilding, geoPnts);

			//the user has specified a start room
			//try to get a route out of the building
			if(!sStartRoom.equals(NO_ROOMS)){
				startBuildingSpaces = mapDbAdapter.getBuildingSpaces(startBuilding.getBuildingid());
				String buildingMapFile = startBuilding.getBuildingid() + ".xml";
				for(int i=0; i<=startBuildingSpaces.size(); i++){
					if(startBuildingSpaces.get(i).getRoom().equals(sStartRoom)){
						startSpace = startBuildingSpaces.get(i);
						break;
					}
				}

				//overlay the start floor spaces
				int floor = startSpace.getFloor();
				for(int i=0; i<startBuildingSpaces.size(); i++){
					if(startBuildingSpaces.get(i).getFloor( )== floor)
						startFloorOverlays.put(startBuildingSpaces.get(i).getRoom(), startBuildingSpaces.get(i).getArrayList());
				}
				mMap.getOverlays().add(new SpaceAreasOverlay(startFloorOverlays, startSpace));	

				try{
					startBuild = parser.parse((InputStream) assets.open(buildingMapFile), startBuildingSpaces);
					routeOutOfStart = routeOutOfStart.getRoute(startSpace, startBuild, new Route());
					addPathToRoute(routeOutOfStart);
					//set start location for outdoor route
					ArrayList<Object> directions = routeOutOfStart.getDirections();
					Exit exitFromStartBuild = (Exit) directions.get(directions.size()-1);
					start = new Loc(exitFromStartBuild.getLat(), exitFromStartBuild.getLong());

				}catch (IOException ex) {
					//Map for building is not in our assets directory
					Toast toast = new Toast(getApplicationContext());
					toast.setDuration(6);
					toast =	Toast.makeText(getApplicationContext(), 
							"The building: " + sEndBuilding + " does not have a map specified. " +
									"Directions will only be to the building area.", toast.getDuration()); 
					toast.show();
					start = averagePoints(startBuilding.getShape());
					startBuild = null;
				} catch (ParserConfigurationException e) {
					e.printStackTrace();
				} catch (SAXException e) {
					e.printStackTrace();
				}
				//our end building has no rooms to get to set end location to average points of this building
			}else{
				start = averagePoints(startBuilding.getShape());
			}
		}

		// the user chose to use their GPS location
		else{ 
			userLoc = (Loc[]) ((MyApplication) this.getApplication()).getUserLocation();
			start = userLoc[0];
			if(endBuild != null)
				addPathToRoute(routeOutOfEnd);
		}

		mMap.getOverlays().add(new AreasOverlay(areaOverlays));
		
		
		//overlay the torches with information
		MyItemizedOverlay overlay = new MyItemizedOverlay(torch, this);	
		GeoPoint spot;

		//Start torch
		String snippet;
		if(sStartBuilding != null){
			snippet = "Starting at " + sStartBuilding;
			if(!sStartRoom.equals(NO_ROOMS)){
				spot = averagePoints(startSpace.getPoints()).toGeoPoint();
				snippet = snippet + " Room " + sStartRoom;
			}
			else
				spot = start.toGeoPoint();
		}
		else{
			//using gps loaction
			spot = start.toGeoPoint();
			snippet = "Your last requested location.";
		}
		OverlayItem item = new OverlayItem(spot, "Start", snippet);
		overlay.addOverlay(item);
		mMap.getOverlays().add(overlay);

		//End torch
		if(endSpace != null)
			spot = averagePoints(endSpace.getPoints()).toGeoPoint();
		else
			spot = end.toGeoPoint();
		snippet = "Arriving at " + sEndBuilding;
		if(!sEndRoom.equals(NO_ROOMS)){
			snippet = snippet + " Room " + sEndRoom;
		}
		item = new OverlayItem(spot, "End", snippet);
		overlay.addOverlay(item);
		mMap.getOverlays().add(overlay);
		
		DecimalFormat sixD = new DecimalFormat("###.######");
		
		if(start != null && end != null){
			double sLat = Double.valueOf(sixD.format(start.getLat())), 
					sLon = Double.valueOf(sixD.format(start.getLon())),
					eLat = Double.valueOf(sixD.format(end.getLat())), 
					eLon = Double.valueOf(sixD.format(end.getLon()));
			outdoorRoute = OutdoorRoute.getDirections(sLat, sLon, eLat, eLon);
		}
		
		mMap.getOverlays().add(new RouteOverlay(route, 1));
		if(outdoorRoute != null)
			mMap.getOverlays().add(new RouteOverlay(outdoorRoute, 0));
		
		mapDbAdapter.close();
	}

	private void addPathToRoute(Route r) {
		ArrayList<Object> directions = r.getDirections();

		for(int i = 0; i<directions.size(); i++){
			Object o = directions.get(i);

			if(o instanceof Exit){
				route.add(((Exit) o).getGeopoint());
				System.out.println(((Exit) o).getExitID());
			}
			if(o instanceof Stairwell){
				route.add(((Stairwell) o).getGeopoint());
				System.out.println(((Stairwell) o).getStairID());
			}
			if(o instanceof Intersection){
				route.add(((Intersection) o).getGeopoint());
				System.out.println(((Intersection) o).getInterID());
			}
			if(o instanceof Space){
				Loc sh = averagePoints(((Space) o).getPoints());
				route.add(new GeoPoint((int) (sh.getLat() * 1e6), (int) (sh.getLon() * 1e6)));
				System.out.println(((Space) o).getRoom());
			}
		}
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * Compute the average location of a given shape
	 * @param shape	the shape, in lat/longs
	 * @return
	 */
	private Loc averagePoints(Loc[] shape) {
		double totalLat = 0;
		double totalLon = 0;
		int numPoints = shape.length;
		for(int i = 0; i < numPoints; i++) {
			totalLat += shape[i].getLat();
			totalLon += shape[i].getLon();
		}
		return new Loc(totalLat/numPoints, totalLon/numPoints);			
	}
	
}














//double fitFactor = 1.5;		
//mMap.getController().zoomToSpan((int) (Math.abs(maxLat - minLat) * fitFactor), (int)(Math.abs(maxLon - minLon) * fitFactor));

